package in.srain.cube.util;

import android.os.Bundle;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import in.srain.cube.request.JsonData;

public class JSONUtils {

    /**
     * Wraps the given object if necessary.
     */
    public static Object wrap(Object o) {
        if (o == null) {
            return JSONObject.NULL;
        }
        if (o instanceof JSONArray || o instanceof JSONObject) {
            return o;
        }
        if (o.equals(JSONObject.NULL)) {
            return o;
        }
        try {
            if (o instanceof Collection) {
                return new JSONArray((Collection) o);
            } else if (o.getClass().isArray()) {
                return toJSONArray(o);
            }
            if (o instanceof Map) {
                return new JSONObject((Map) o);
            }
            if (o instanceof Boolean ||
                    o instanceof Byte ||
                    o instanceof Character ||
                    o instanceof Double ||
                    o instanceof Float ||
                    o instanceof Integer ||
                    o instanceof Long ||
                    o instanceof Short ||
                    o instanceof String) {
                return o;
            }
            if (o.getClass().getPackage().getName().startsWith("java.")) {
                return o.toString();
            }
        } catch (Exception ignored) {
        }
        return null;
    }

    /**
     * Returns an array with the values corresponding to {@code array}. The
     * array contains null for array that aren't mapped. This method returns
     * null if {@code array} is either null or empty.
     */
    public static JSONArray toJSONArray(Object array) throws JSONException {
        if (!array.getClass().isArray()) {
            throw new JSONException("Not a primitive array: " + array.getClass());
        }
        JSONArray result = new JSONArray();
        final int length = Array.getLength(array);
        for (int i = 0; i < length; ++i) {
            result.put(wrap(Array.get(array, i)));
        }
        return result;
    }

    public static JSONObject bundle2Json(Bundle bundle) {
        JSONObject json = new JSONObject();
        if (bundle == null) {
            return json;
        }
        Set<String> keys = bundle.keySet();
        for (String key : keys) {
            try {
                json.put(key, JSONUtils.wrap(bundle.get(key)));
            } catch (JSONException e) {
            }
        }
        return json;
    }

    public static Map<String, Object> toMap(JSONObject object) throws JSONException {
        Map<String, Object> map = new HashMap<>();
        Iterator keys = object.keys();
        while (keys.hasNext()) {
            String key = (String) keys.next();
            map.put(key, fromJson(object.get(key)));
        }
        return map;
    }

    public static <T> Map<String, T> toMap(JsonData jsonData, Class<T> tClass) throws JSONException, ClassCastException {
        Map<String, T> map = new HashMap<>();
        Iterator iterator = jsonData.keys();
        while (iterator.hasNext()) {
            String key = (String) iterator.next();
            map.put(key, (T) fromJson(jsonData.get(key)));
        }
        return map;
    }

    public static Bundle toBooleanBundle(JsonData jsonData) {
        Bundle params = new Bundle();
        Iterator keys = jsonData.keys();
        try {
            while (keys.hasNext()) {
                String key = (String) keys.next();
                params.putBoolean(key, (Boolean) fromJson(jsonData.get(key)));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return params;
    }

    public static List toList(JSONArray array) throws JSONException {
        List<Object> list = new ArrayList<>();
        for (int i = 0; i < array.length(); i++) {
            list.add(fromJson(array.get(i)));
        }
        return list;
    }

    private static Object fromJson(Object json) throws JSONException {
        if (json == JSONObject.NULL) {
            return null;
        } else if (json instanceof JSONObject) {
            return toMap((JSONObject) json);
        } else if (json instanceof JSONArray) {
            return toList((JSONArray) json);
        } else {
            return json;
        }
    }

    public static String fromIterableToString(Iterator<?> source) {
        if (source == null) {
            return "";
        }
        StringBuilder valueBuilder = new StringBuilder();
        while (source.hasNext()) {
            valueBuilder.append(source.next()).append(",");
        }
        if (valueBuilder.length() > 0) {
            valueBuilder.deleteCharAt(valueBuilder.length() - 1);
        }
        return valueBuilder.toString();
    }
}
